package com.cr.front.order.service.impl;


import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cr.common.exception.ServiceException;
import com.cr.common.response.StatusCode;
import com.cr.front.authentication.pojo.vo.AuthenticationVO;
import com.cr.front.authentication.pojo.vo.DriverLicenseVO;
import com.cr.front.authentication.service.AuthenticationService;
import com.cr.front.authentication.service.DriverLicenseService;
import com.cr.front.cost.pojo.dto.param.CalCostParam;
import com.cr.front.cost.service.CostService;
import com.cr.front.cost.service.impl.CostServiceImpl;
import com.cr.front.order.dao.mapper.OrderMapper;
import com.cr.front.order.pojo.TimeRange;
import com.cr.front.order.pojo.dto.*;
import com.cr.front.order.pojo.entity.Order;
import com.cr.front.order.pojo.vo.OrderVO;
import com.cr.front.order.service.OrderService;
import com.cr.front.vehicle.dao.mapper.VehicleMapper;
import com.cr.front.vehicle.pojo.entity.Vehicle;
import com.cr.front.vehicle.pojo.vo.VehicleVO;
import com.cr.front.vehicle.service.VehicleService;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Transactional
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private CostService costService;
    @Autowired
    private DriverLicenseService driverLicenseService;
    @Autowired
    private AuthenticationService authenticationService;
    @Autowired
    private VehicleService vehicleService;
    @Autowired
    private VehicleMapper vehicleMapper;
    @Override
    public String saveOrder(OrderSaveParam orderSaveParam) {
        String orderId = null;
        boolean flag = false;
        //下单前需要有 1) 通过审核且有效的驾照 2)通过审核且有效的证件
        DriverLicenseVO driverLicenseDetail = driverLicenseService.getDriverLicenseDetail();
        AuthenticationVO authenticationDetail = authenticationService.getAuthenticationDetail();
        if(driverLicenseDetail != null && authenticationDetail != null){
            //判断驾照和证件是否有效
            if(driverLicenseDetail.getValidationStatus() == 0 && authenticationDetail.getStatus() == 0){
                //判断审核状态通过
                if(driverLicenseDetail.getAuditing()==0 && authenticationDetail.getAuditing()==0){
                    flag=true;
                    Long id = null;
                    String obj = StpUtil.getLoginId().toString();
                    if (obj != null) {
                        id = Long.parseLong(obj);
                    }
                    if(id == null){
                        throw new ServiceException("未登录");
                    }

                    Order order = new Order();
                    BeanUtils.copyProperties(orderSaveParam, order);
                    order.setUserId(id);
                    // 生成随机订单号，假设你希望订单号保持唯一性
                    orderId = generateUniqueOrderNumber();
                    order.setId(orderId);

                    //设置默认状态为待支付3
                    order.setOrderStatus("3");
                    //转换String yyyy-MM-dd日期 到 Date
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    Date startDate;
                    Date EndDate;

                    try {
                        startDate = sdf.parse(orderSaveParam.getStartDate());
                        EndDate = sdf.parse(orderSaveParam.getEndDate());
                        order.setStartDate(startDate);
                        order.setEndDate(EndDate);

                    } catch (ParseException e) {
                        throw new RuntimeException(e);
                    }

                    //根据传过来的日期与时间戳租车,起始时间至租车结束时间计算出租车时长
                    long rentalDuration = daysBetween(startDate, EndDate);
                    order.setRentalDuration(rentalDuration);
                    //获得正确的车辆可用时间, 车辆提供的可用时间 减去 订单占用的时间
                    List<List<TimeRange>> lists = filterAvailableTime(orderSaveParam.getVehicleId());
                    log.debug("filterAvailableTime : {}",lists);

                    LocalDate startLocalDate = startDate.toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDate();
                    LocalDate endLocalDate = EndDate.toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDate();
                    //判断订单时间是否合法
                    if(isOrderTimeLegal(startLocalDate, endLocalDate, lists)){

                        //调用costService计算租车费用
                        CalCostParam costParam = new CalCostParam();
                        costParam.setVehicleId(orderSaveParam.getVehicleId());

                        // 使用format方法将Date对象转换为字符串
                        costParam.setStartDate(orderSaveParam.getStartDate());
                        costParam.setEndDate(orderSaveParam.getEndDate());

                        order.setTotalCost(costService.calculateRentCost(costParam).getDiscountCost());


                        log.warn("订单信息：{}", order);
                        orderMapper.insert(order);

                    }else {
                        throw new RuntimeException("订单时间冲突或不合法");
                    }
                }
            }
        }
        if(!flag){
            throw new ServiceException("请先完善您的证件信息或驾照信息");
        }
        return orderId;
    }
    //判断订单时间是否合法
    private boolean isOrderTimeLegal(LocalDate start, LocalDate end, List<List<TimeRange>> filterAvailableTime){
        for(List<TimeRange> timeRanges : filterAvailableTime){
            for(TimeRange timeRange : timeRanges){
                if(!start.isBefore(timeRange.getStartDate()) && !end.isAfter(timeRange.getEndDate())){
                    return true;
                }
            }
        }
        return false;
    }
    //获取可用时间list
    private List<List<TimeRange>> filterAvailableTime(Long VehicleId) {
        //返回list
        List<List<TimeRange>> result = new ArrayList<>();
        LocalDate today = LocalDate.now();

        //查询这两车的进行中订单
        OrderQuery orderQuery = new OrderQuery();
        orderQuery.setVehicleId(VehicleId);
        Long[] orderStatus = new Long[]{1L, 3L};
        orderQuery.setOrderStatus(Arrays.asList(orderStatus));
        List<OrderVO> orderVOS = listOrderByVehicleId(orderQuery);
        log.debug("orderVOS:{}", orderVOS);
        //拿到进行中订单的用车时间
        List<TimeRange> orderUsedTimes = new ArrayList<>();
        for(OrderVO orderVO : orderVOS){
            LocalDate start = orderVO.getStartDate().toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
            LocalDate end = orderVO.getEndDate().toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
            if(start.isBefore(today)){
                if(end.isBefore(today)){
                    continue;
                }
                start = today;
            }

            orderUsedTimes.add(new TimeRange(start, end));
        }
        // 排序时间范围
        orderUsedTimes.sort(Comparator.comparing(TimeRange::getStartDate));
        log.debug("orderUsedTimes:{}", orderUsedTimes);


        //拿到车辆可用时间
        VehicleVO vehicleDetail = vehicleService.getDetail(VehicleId);
        String availableTime = vehicleDetail.getAvailableTime();
        String trimmedString = availableTime.substring(1, availableTime.length() - 1);
        String[] elements = trimmedString.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
        List<TimeRange> availableTimes = new ArrayList<>();
        for(String element : elements){

            JSONObject jsonObject = new JSONObject(element);
            String period = jsonObject.getStr("period");
            String[] periodArr = period.split(",");
            //时间段开始和结束
            LocalDate totalStart = LocalDate.parse(periodArr[0]);
            LocalDate totalEnd = LocalDate.parse(periodArr[1]);
            //只计算今天之后的可用时间
            if(totalStart.isBefore(today)){
                if(totalEnd.isBefore(today)){
                    continue;
                }
                totalStart = today;
            }

            availableTimes.add(new TimeRange(totalStart, totalEnd));
//            List<TimeRange> availableRanges = findAvailableRanges(orderUsedTimes, totalStart, totalEnd);
//            result.add(availableRanges);
       }

        return findAvailableRanges(orderUsedTimes, availableTimes);

   }

    private static List<List<TimeRange>> findAvailableRanges(List<TimeRange> orderUsedTimes, List<TimeRange> availableTimes) {
        List<List<TimeRange>> result = new ArrayList<>();
        for(TimeRange range : availableTimes){
            List<TimeRange> availableRanges = new ArrayList<>();
            availableRanges.add(range);
            result.add(availableRanges);
        }
        log.debug("result before:{}", result);
        for(List<TimeRange> availableRanges : result){

            for(TimeRange orderUsedTime : orderUsedTimes){
                //新添加的时间段
                List<TimeRange> temp = new ArrayList<>();
                Iterator<TimeRange> iterator = availableRanges.iterator();
                while (iterator.hasNext()){
                    TimeRange range = iterator.next();
                    if(!orderUsedTime.getStartDate().isBefore(range.getStartDate()) && ! orderUsedTime.getEndDate().isAfter(range.getEndDate())){
                        log.debug("hit {} in {}", orderUsedTime, range);
                        if(!orderUsedTime.getStartDate().isEqual(range.getStartDate()) &&  orderUsedTime.getEndDate().isEqual(range.getEndDate())){
                            temp.add(new TimeRange(range.getStartDate(), orderUsedTime.getStartDate().minusDays(1)));
                        }else if(orderUsedTime.getStartDate().isEqual(range.getStartDate()) &&  !orderUsedTime.getEndDate().isEqual(range.getEndDate())){
                            temp.add(new TimeRange(orderUsedTime.getEndDate().plusDays(1), range.getEndDate()));
                        }else if(!orderUsedTime.getStartDate().isEqual(range.getStartDate()) &&  !orderUsedTime.getEndDate().isEqual(range.getEndDate())){
                            temp.add(new TimeRange(range.getStartDate(), orderUsedTime.getStartDate().minusDays(1)));
                            temp.add(new TimeRange(orderUsedTime.getEndDate().plusDays(1), range.getEndDate()));
                        }

                        iterator.remove();
                    }
                }
                availableRanges.addAll(temp);

            }

        }
        log.debug("result after {}",result);

        return result;
    }



    @Override
    public List<OrderVO> listOrderByVehicleId(OrderQuery orderQuery) {
        LocalDateTime today = LocalDateTime.now();

        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        List<Long> orderStatus = orderQuery.getOrderStatus();
        if(orderStatus!=null && !orderStatus.isEmpty()){
            queryWrapper.in(Order::getOrderStatus, orderStatus);
        }
        queryWrapper.gt(Order::getEndDate, today);
        queryWrapper.eq(Order::getVehicleId, orderQuery.getVehicleId());
        List<Order> orders = orderMapper.selectList(queryWrapper);
        return getOrderVOS(orders);

    }

    @Override
    public List<OrderVO> listOrderByUSerId(UserOrderQuery userOrderQuery) {
        Long id = null;
        String obj = StpUtil.getLoginId().toString();
        if (obj != null) {
            id = Long.parseLong(obj);
        }
        if(id!=null){
            userOrderQuery.setUserId(id);
            return orderMapper.selectOrderByUSerId(userOrderQuery);
        }
        return null;
    }

    @Override
    public List<List<TimeRange>> listAvailableTime(OrderQuery orderQuery) {
        return filterAvailableTime(orderQuery.getVehicleId());


    }

    @Override
    public OrderVO getPaymentOrderDetail(String orderId) {
        //验证订单是否当前登录用户的订单
        long userId = getUserId();
        OrderVO orderVO = new OrderVO();
        Order order = orderMapper.selectById(orderId);
        if(order==null){
            throw new ServiceException(StatusCode.ORDER_PAYMENT_FAILURE,"订单不存在");
        }
        if(order.getUserId() != userId){
            throw new ServiceException(StatusCode.ORDER_PAYMENT_FAILURE,"订单不属于当前用户");
        }
        if(!"3".equals(order.getOrderStatus())){
            throw new ServiceException(StatusCode.ORDER_PAYMENT_FAILURE, "订单不需要支付");
        }
        BeanUtils.copyProperties(order, orderVO);
        Vehicle vehicle = vehicleMapper.selectById(order.getVehicleId());
        orderVO.setBrandId(vehicle.getBrandId());
        orderVO.setBrandInfo(vehicle.getBrandInfo());
        orderVO.setImgs(vehicle.getImgs());
        orderVO.setPlateNumber(vehicle.getPlateNumber());

        return orderVO;
    }

    @Override
    public void payOrder(String orderId, PayOrderParam payOrderParam) {

        UpdateWrapper<Order> updateWrapper= new UpdateWrapper<>();
        updateWrapper.eq("id", orderId);
        updateWrapper.set("payment_method",payOrderParam.getPaymentMethod());
        updateWrapper.set("trade_no",payOrderParam.getTradeNo());
        //支付状态 已完成
        updateWrapper.set("payment_status", "1");
        updateWrapper.set("payment_date",new Date());
        updateWrapper.set("order_status","1");

        orderMapper.update(null,updateWrapper);

    }

    private static long getUserId() {
        //验证订单是否当前登录用户的订单
        long userId;
        String obj = StpUtil.getLoginId().toString();
        if (obj != null) {
            userId = Long.parseLong(obj);
        }else {
            throw new ServiceException(StatusCode.ORDER_PAYMENT_FAILURE,"未登录");
        }
        return userId;
    }

    @Override
    public void cancelOrder(String orderId, OrderCancelParam orderCancelParam) {
        //只要不是已完成或已取消都能取消
        long userId = getUserId();
        Order order = orderMapper.selectById(orderId);
        if(order.getUserId() != userId){
            throw new ServiceException("订单不属于当前用户");
        }
        if(!"1".equals(order.getOrderStatus()) && !"3".equals(order.getOrderStatus())){
            throw new ServiceException( "订单已完成或已取消, 无法取消");
        }
        UpdateWrapper<Order> updateWrapper= new UpdateWrapper<>();
        updateWrapper.eq("id", orderId);
        updateWrapper.set("order_status","0");
        updateWrapper.set("cancellation_time",new Date());
        updateWrapper.set("cancellation_reason", orderCancelParam.getCancellationReason());
        orderMapper.update(null,updateWrapper);
        //TODO 处理退钱

    }

    @Override
    public void completeOrder(String orderId) {
        long userId = getUserId();
        Order order = orderMapper.selectById(orderId);
        if(order.getUserId() != userId){
            throw new ServiceException("订单不属于当前用户");
        }
        if(!"1".equals(order.getOrderStatus())){
            throw new ServiceException("订单状态错误无法还车");
        }
        UpdateWrapper<Order> updateWrapper= new UpdateWrapper<>();
        updateWrapper.eq("id", orderId);
        updateWrapper.set("order_status","2");
        orderMapper.update(null,updateWrapper);



    }

    @Override
    public OrderVO getOrderDetail(String orderId) {
        long userId = getUserId();
        OrderVO orderVO = new OrderVO();
        Order order = orderMapper.selectById(orderId);
        if(order==null){
            throw new ServiceException("订单不存在");
        }
        BeanUtils.copyProperties(order, orderVO);
        Vehicle vehicle = vehicleMapper.selectById(order.getVehicleId());
        orderVO.setBrandId(vehicle.getBrandId());
        orderVO.setBrandInfo(vehicle.getBrandInfo());
        orderVO.setImgs(vehicle.getImgs());
        orderVO.setPlateNumber(vehicle.getPlateNumber());

        return orderVO;
    }

    @Override
    public String validatePayment(String orderId) {

        Order order = orderMapper.selectById(orderId);
        String paymentStatus = order.getPaymentStatus();
        //  如果 支付状态不是 1:已支付
        if(!"1".equals(paymentStatus) ){
            //支付状态改为 2:支付失败
            paymentStatus="2";
            UpdateWrapper<Order> updateWrapper= new UpdateWrapper<>();
            updateWrapper.eq("id", orderId);
            updateWrapper.set("order_status","3");
            updateWrapper.set("payment_status",paymentStatus);
            orderMapper.update(null, updateWrapper);

        }
        return paymentStatus;

    }

    private static List<OrderVO> getOrderVOS(List<Order> orders) {
        List<OrderVO> orderVOS = new ArrayList<>();
        for( Order order : orders){
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(order, orderVO);
            orderVOS.add(orderVO);
        }
        return orderVOS;
    }


    // 生成唯一的订单号
    private String generateUniqueOrderNumber() {
        // 这里可以根据业务需求生成唯一的订单号，例如使用数据库的序列、UUID、时间戳+随机数等方式生成
        Random random = new Random();
        long currentTime = System.currentTimeMillis();
        long orderNumber = currentTime * 1000 + random.nextInt(1000); // 保证足够唯一性
        return String.valueOf(orderNumber);
    }

    // 计算租车时长的方法，假设 startTime 和 endTime 是 Date 类型
    private long calculateRentalDuration(Date startTime, Date endTime) {

        long startMillis = startTime.getTime();
        long endMillis = endTime.getTime();
        return( (endMillis - startMillis) / (1000 * 60 * 60 * 24) +1 ); // 返回租车时长，单位为天数
    }

    public static long daysBetween(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);

        long timeDiff = Math.abs(cal2.getTimeInMillis() - cal1.getTimeInMillis());
        return TimeUnit.DAYS.convert(timeDiff, TimeUnit.MILLISECONDS);

    }


}
